home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / packagetool / packagewindow.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  34.8 KB  |  967 lines

  1. /*
  2.     File: PackageWindow.c
  3.     
  4.     Description:
  5.         Routines for managing the PackageWindow displayed by the
  6.     PackageTool application.  This file also contains the routines used
  7.     for converting folders into packages and packages back into folders.
  8.  
  9.     Copyright:
  10.         © Copyright 1999 Apple Computer, Inc. All rights reserved.
  11.     
  12.     Disclaimer:
  13.         IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  14.         ("Apple") in consideration of your agreement to the following terms, and your
  15.         use, installation, modification or redistribution of this Apple software
  16.         constitutes acceptance of these terms.  If you do not agree with these terms,
  17.         please do not use, install, modify or redistribute this Apple software.
  18.  
  19.         In consideration of your agreement to abide by the following terms, and subject
  20.         to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  21.         copyrights in this original Apple software (the "Apple Software"), to use,
  22.         reproduce, modify and redistribute the Apple Software, with or without
  23.         modifications, in source and/or binary forms; provided that if you redistribute
  24.         the Apple Software in its entirety and without modifications, you must retain
  25.         this notice and the following text and disclaimers in all such redistributions of
  26.         the Apple Software.  Neither the name, trademarks, service marks or logos of
  27.         Apple Computer, Inc. may be used to endorse or promote products derived from the
  28.         Apple Software without specific prior written permission from Apple.  Except as
  29.         expressly stated in this notice, no other rights or licenses, express or implied,
  30.         are granted by Apple herein, including but not limited to any patent rights that
  31.         may be infringed by your derivative works or by other works in which the Apple
  32.         Software may be incorporated.
  33.  
  34.         The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  35.         WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  36.         WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  37.         PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  38.         COMBINATION WITH YOUR PRODUCTS.
  39.  
  40.         IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  41.         CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  42.         GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.         ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  44.         OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  45.         (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  46.         ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  
  48.     Change History (most recent first):
  49.         Wed, Dec 22, 1999 -- created
  50. */
  51.  
  52.  
  53.  
  54. #include "PackageWindow.h"
  55. #include "PackageTool.h"
  56. #include "PackageUtils.h"
  57. #include "Utilities.h"
  58. #include <Drag.h>
  59. #include <Controls.h>
  60. #include <Dialogs.h>
  61. #include <Appearance.h>
  62. #include <Icons.h>
  63. #include <Sound.h>
  64. #include <TextUtils.h>
  65. #include <PLStringFuncs.h>
  66. #include <Navigation.h>
  67. #include <ControlDefinitions.h>
  68.  
  69.  
  70.  
  71.  
  72.  
  73. enum {    /* picture displayed when selection is empty */
  74.     kSpashPictResource = 128
  75. };
  76.  
  77.  
  78.  
  79.     /* main window constants */
  80. enum {
  81.     kPackageDialogResource = 128,
  82.     kPackageUserItem = 1,
  83.     kFolderItem = 2,
  84.     kPackageItem = 3,
  85.     kSelectButtonItem = 4,
  86.         /* kFolderIsPackage and kFolderIsFolder are used for
  87.         internal tracking of the state of the kFolderItem and
  88.         the kPackageItem dialog controls. */
  89.     kFolderIsPackage = 100,    /* item being displayed is a package */
  90.     kFolderIsFolder = 101    /* item being displayed is a folder */
  91. };
  92.  
  93.  
  94.  
  95.     /* variables used to record properties of the main window */
  96. DialogPtr gPackageWindow = NULL; /* a pointer to the main dialog */
  97. PicHandle gSplashPict; /* splash image displayed in gIconBox when there is no file selected */
  98. Rect gIconBox; /* area in the window where the information about the current file is drawn */ 
  99. Rect gIconImage; /* area inside of gIconBox where the icon is drawn */
  100. ControlHandle gPackageButton; /* control for setting gFolderTypeSelection to kFolderIsPackage */
  101. ControlHandle gFolderButton; /* control for setting gFolderTypeSelection to kFolderIsFolder */
  102. ControlHandle gSelectButton; /* control providing command equivalence to the select file menu command */
  103.  
  104.  
  105.  
  106.     /* variables used to record information about the file being displayed */
  107. Boolean gFileInDisplay = false; /* true when gFileAlias contains an alias handle */
  108. AliasHandle gFileAlias = NULL; /* an alias to the last file dragged into the gIconBox */
  109. IconRef gIconRef = NULL; /* an icon services reference to an icon for the file */
  110. short gFolderTypeSelection = kFolderIsFolder; /* determines the type of data we will provide for drags */
  111. Boolean gPWActive = false;
  112.  
  113.  
  114.  
  115.     /* these variables are used both in the receive handler and inside of the
  116.         tracking handler.  The following variables are shared between
  117.         MyDragTrackingHandler and MyDragReceiveHandler.  */
  118. static Boolean gApprovedDrag = false; /* set to true if the drag is approved */
  119. static Boolean gInIconBox = false; /* set true if the drag is inside our drop box */
  120.  
  121.  
  122.  
  123.     /* procedure pointers used in the main window */
  124. DragReceiveHandlerUPP gMainReceiveHandler = NULL; /* receive handler for the main dialog */
  125. DragTrackingHandlerUPP gMainTrackingHandler = NULL; /* tracking handler for the main dialog */
  126. UserItemUPP myUserItem; /* UPP for the icon's user item.  This routine draws into gIconBox */
  127.  
  128.  
  129.  
  130.  
  131. Boolean IsPackageWindow(WindowPtr target) {
  132.     return (target == GetDialogWindow(gPackageWindow));
  133. }
  134.  
  135.  
  136.  
  137.  
  138. /* ApproveDragReference is called by the drag tracking handler to determine
  139.     if the contents of the drag can be handled by our receive handler.
  140.     here, we only allow packages and folders.  */
  141. static pascal OSErr ApproveDragReference(DragReference theDragRef, Boolean *approved) {
  142.     OSErr err;
  143.     UInt16 itemCount;
  144.     DragAttributes dragAttrs;
  145.     ItemReference theItem;
  146.     HFSFlavor targetFile;
  147.     long theSize;
  148.     
  149.         /* we cannot drag to our own window */
  150.     if ((err = GetDragAttributes(theDragRef, &dragAttrs)) != noErr) goto bail;
  151.     if ((dragAttrs & kDragInsideSenderWindow) != 0) { err = userCanceledErr; goto bail; }
  152.     
  153.         /* we only accept drags containing one item */
  154.     if ((err = CountDragItems(theDragRef, &itemCount)) != noErr) goto bail;
  155.     if (itemCount != 1) { err = paramErr; goto bail; }
  156.         
  157.         /* gather information about the drag & a reference to item one. */
  158.     if ((err = GetDragItemReferenceNumber(theDragRef, 1, &theItem)) != noErr) goto bail;
  159.         
  160.         /* try to get a  HFSFlavor*/
  161.     theSize = sizeof(HFSFlavor);
  162.     err = GetFlavorData(theDragRef, theItem, flavorTypeHFS, &targetFile, &theSize, 0);
  163.     if (err != noErr) goto bail;
  164.  
  165.         /* it must be a folder or a package */
  166.     if (IdentifyPackage(&targetFile.fileSpec, NULL))
  167.         *approved = true;
  168.     else if (targetFile.fileCreator == 'MACS' && targetFile.fileType == 'fold')
  169.         *approved = true;
  170.     else {
  171.         err = paramErr;
  172.         goto bail;
  173.     }
  174.                 
  175.     return noErr;
  176. bail:
  177.         /* an error occured, clean up.  set result to false. */
  178.     *approved = false;
  179.     return err;
  180. }
  181.  
  182.  
  183.  
  184.  
  185. /* MyDragTrackingHandler is called for tracking the mouse while a drag is passing over our
  186.     window.  if the drag is approved, then the drop box will be hilitied appropriately
  187.     as the mouse passes over it.  */
  188. static pascal OSErr MyDragTrackingHandler(DragTrackingMessage message, WindowPtr theWindow, void *refCon, DragReference theDragRef) {
  189.         /* we're drawing into the image well if we hilite... */
  190.     switch (message) {
  191.     
  192.         case kDragTrackingEnterWindow:
  193.             {    Point mouse;
  194.                 gApprovedDrag = false;
  195.                 if (theWindow == FrontWindow()) {
  196.                     if (ApproveDragReference(theDragRef, &gApprovedDrag) != noErr) break;
  197.                     if ( ! gApprovedDrag ) break;
  198.                     SetPort(GetWindowPort(theWindow));
  199.                     GetMouse(&mouse);
  200.                     if (PtInRect(mouse, &gIconBox)) {  /* if we're in the box, hilite... */
  201.                         gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr);
  202.                     }
  203.                 }
  204.             }
  205.             break;
  206.  
  207.         case kDragTrackingInWindow:
  208.             if (gApprovedDrag) {
  209.                 Point mouse;
  210.                 SetPort(GetWindowPort(theWindow));
  211.                 GetMouse(&mouse);
  212.                 if (PtInRect(mouse, &gIconBox)) {
  213.                     if ( ! gInIconBox) {  /* if we're entering the box, hilite... */
  214.                         gInIconBox = (ShowDragHiliteBox(theDragRef, &gIconBox) == noErr);
  215.                     }
  216.                 } else if (gInIconBox) {  /* if we're exiting the box, unhilite... */
  217.                     HideDragHilite(theDragRef);
  218.                     gInIconBox = false;
  219.                 }
  220.             }
  221.             break;
  222.  
  223.         case kDragTrackingLeaveWindow:
  224.             if (gApprovedDrag && gInIconBox) {
  225.                 HideDragHilite(theDragRef);
  226.             }
  227.             gApprovedDrag = gInIconBox = false;
  228.             break;
  229.     }
  230.     return noErr; // there's no point in confusing Drag Manager or its caller
  231. }
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238. /* MyDragReceiveHandler receives drags that are dropped into the
  239.     main window.  here, we only receive packages or folders. */
  240. static pascal OSErr MyDragReceiveHandler(WindowPtr theWindow, void *refcon, DragReference theDragRef) {
  241.     ItemReference theItem;
  242.     HFSFlavor targetFile;
  243.     Size theSize;
  244.     OSErr err;
  245.     
  246.         /* validate the drag.  Recall the receive handler will only be called after
  247.         the tracking handler has received a kDragTrackingInWindow event.  As a result,
  248.         the gApprovedDrag and gInIconBox will be defined when we arrive here.  Hence,
  249.         there is no need to spend extra time validating the drag at this point. */
  250.     if ( ! (gApprovedDrag && gInIconBox) ) { err = userCanceledErr; goto bail; }
  251.     
  252.         /* get the first item reference */
  253.     err = GetDragItemReferenceNumber(theDragRef, 1, &theItem);
  254.     if (err != noErr) goto bail;
  255.         
  256.         /* try to get a  HFSFlavor*/
  257.     theSize = sizeof(HFSFlavor);
  258.     err = GetFlavorData(theDragRef, theItem, flavorTypeHFS, &targetFile, &theSize, 0);
  259.     if (err != noErr) goto bail;
  260.     
  261.         /* display the located file*/
  262.     SetNewDisplay(&targetFile.fileSpec);
  263.     return noErr;
  264. bail:
  265.     return err;
  266. }
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274. /* MyNavFilterProc This is the filter function we provide for the Navigation services
  275.     dialogs. */
  276. static pascal Boolean MyNavFilterProc( AEDesc* theItem, void* info, NavCallBackUserData callBackUD, NavFilterModes filterMode) {
  277.     NavFileOrFolderInfo* theInfo;
  278.     if ( theItem->descriptorType == typeFSS ) {
  279.         theInfo = (NavFileOrFolderInfo*) info;
  280.         return theInfo->isFolder;
  281.     }
  282.     return true;
  283. }
  284.  
  285.  
  286.  
  287.  
  288. /* NavEventCallBack is the event handling call back we provide for Navigation
  289.     services.  It's presence is important so our windows will be updated appropriately
  290.     when the navigation window is resized or moved. */
  291. static pascal void NavEventCallBack( NavEventCallbackMessage callBackSelector,
  292.             NavCBRecPtr callBackParms, NavCallBackUserData callBackUD) {
  293.     if (callBackSelector == kNavCBEvent) {
  294.         EventRecord *ev;
  295.         ev = callBackParms->eventData.eventDataParms.event;
  296.         if (ev->what == updateEvt || ev->what == activateEvt)
  297.             HandleNextEvent(ev);
  298.     }
  299. }
  300.  
  301.  
  302.  
  303.  
  304. /* SelectFolderOrPackage provides user interaction through calling
  305.     navigation services that allows the user to choose a package or
  306.     a folder for display in the main window.   */
  307. OSStatus SelectFolderOrPackage(void) {
  308.     NavDialogOptions dialogOptions;
  309.     NavReplyRecord theReply;
  310.     NavEventUPP eventf;
  311.     NavObjectFilterUPP filterf;
  312.     Boolean hasreply;
  313.     OSStatus err;
  314.     AEDescList documents;
  315.     Boolean watchCursorON;
  316.     Cursor theArrow;
  317.         /* set up locals */
  318.     eventf = NULL;
  319.     filterf = NULL;
  320.     hasreply = false;
  321.     watchCursorON = false;
  322.     BlockZero(&theReply, sizeof(theReply));
  323.     AECreateDesc(typeNull, NULL, 0, &documents);
  324.     GetQDGlobalsArrow(&theArrow);
  325.         /* Let's put up the watch cursor while we *wait* for the navigation
  326.         services window to appear. */
  327.     SetCursor(*GetCursor(watchCursor));
  328.     watchCursorON = true;
  329.         /* allocate data */
  330.     filterf = NewNavObjectFilterUPP(MyNavFilterProc);
  331.     if (filterf == NULL) { err = memFullErr; goto bail; }
  332.     eventf = NewNavEventProc(NavEventCallBack);
  333.     if (eventf == NULL) { err = memFullErr; goto bail; }
  334.         /* set up dialog options */
  335.     err = NavGetDefaultDialogOptions(&dialogOptions);
  336.     if (err != noErr) goto bail;
  337.         /* NOTE: Setting the kNavSupportPackages flag allows us to
  338.         select Mac OS 9 packages. */
  339.     dialogOptions.dialogOptionFlags = (kNavDontAutoTranslate | kNavSupportPackages);
  340.         /* pick one or more files */
  341.     err = NavChooseObject(NULL, &theReply, &dialogOptions, eventf, filterf,  NULL);
  342.         /* reset the cursor to a known state*/
  343.     SetCursor(&theArrow);
  344.     watchCursorON = false;
  345.         /* dispatch on our nav services reply */
  346.     if (err != noErr) goto bail;
  347.     if (!theReply.validRecord) { err = userCanceledErr; goto bail; }
  348.     hasreply = true;
  349.         /* standardize the document list returned by navigation services.
  350.         Note:
  351.         when you select a package in Navigation Services, it it will be returned
  352.         as if it were a folder.  And, in Nav Services replies, that means the
  353.         FSSpec referring to the folder will contain a zero length name and
  354.         the directory ID of the folder.  The following routine automatically
  355.         coerces all such Nav-format-FSSpecs into FSMakeFSSpec format
  356.         and stores them into a list that has the same format as the list
  357.         passed to your 'odoc' Apple event handler.  By doing this, we only
  358.         need to have one OpenDocumentList routine in our application.  */
  359.     err = NavReplyToODOCList(&theReply.selection, &documents);
  360.     if (err != noErr) goto bail;
  361.         /* if we have a valid reply, then call our
  362.         open documents routine. */
  363.     err = OpenDocumentList(&theReply.selection);
  364.     if (err != noErr) goto bail;
  365.         /* clean up the structures we allocated */
  366.     NavDisposeReply(&theReply);
  367.     DisposeNavEventUPP(eventf);
  368.     DisposeNavObjectFilterUPP(filterf);
  369.     return noErr;
  370. bail:
  371.     AEDisposeDesc(&documents);
  372.     if (hasreply) NavDisposeReply(&theReply);
  373.     if (eventf != NULL) DisposeNavEventUPP(eventf);
  374.     if (filterf != NULL) DisposeNavObjectFilterUPP(filterf);
  375.     if (watchCursorON) SetCursor(&theArrow);
  376.     return err;
  377. }
  378.  
  379.  
  380.  
  381.  
  382. /* SetNewDisplay is called to set the file or folder being displayed in the main window.
  383.     Here, structures are deallocated and an alias is saved referring to the file. 
  384.     SetNewDisplay is called from the drag receive handler and since it is not
  385.     safe to call "GetIconSuiteFromFinder()" from inside of the drag receive handler
  386.     (it uses apple events), the flag is used to defer that operation
  387.     until the next time ValidateFDPWindowDisplay.  ValidateFDPWindowDisplay is
  388.     called from the main loop.  If targetFile is NULL, then the display is cleared. */
  389. void SetNewDisplay(FSSpec *targetFile) {
  390.     SInt16 theLabel;
  391.     FSSpec mainPackageFile;
  392.         /* remove the old file */
  393.     if (gFileInDisplay) {
  394.         DisposeHandle((Handle) gFileAlias);
  395.         gFileAlias = NULL;
  396.         ReleaseIconRef(gIconRef);
  397.         gIconRef = NULL;
  398.         gFileInDisplay = false;
  399.         InvalWindowRect(GetDialogWindow(gPackageWindow), &gIconBox);
  400.     }
  401.         /* if there's no new file, we're done */
  402.     if (targetFile == NULL) goto bail;
  403.         /* create the new alias */
  404.     if (NewAliasMinimal(targetFile, &gFileAlias) != noErr) goto bail;
  405.         /* determine the kind of object we're displaying */
  406.     if (IdentifyPackage(targetFile, &mainPackageFile)) {
  407.         if (GetIconRefFromFile(&mainPackageFile, &gIconRef, &theLabel) != noErr) goto bail;
  408.         gFolderTypeSelection = kFolderIsPackage;
  409.     } else {
  410.         if (GetIconRefFromFile(targetFile, &gIconRef, &theLabel) != noErr) goto bail;
  411.         gFolderTypeSelection = kFolderIsFolder;
  412.     }
  413.         /* set up the controls */
  414.     SetControlValue(gPackageButton, (gFolderTypeSelection == kFolderIsPackage ? 1 : 0));
  415.     SetControlValue(gFolderButton, (gFolderTypeSelection == kFolderIsFolder ? 1 : 0));
  416.     if (gPWActive) {
  417.         ActivateControl(gPackageButton);
  418.         ActivateControl(gFolderButton);
  419.     } else {
  420.         DeactivateControl(gPackageButton);
  421.         DeactivateControl(gFolderButton);
  422.     }
  423.         /* update the window */
  424.     InvalWindowRect(GetDialogWindow(gPackageWindow), &gIconBox);
  425.         /* set the flag and return */
  426.     gFileInDisplay = true;
  427.     return;
  428. bail:
  429.     if (gFileAlias != NULL) DisposeHandle((Handle) gFileAlias);
  430.     if (gIconRef != NULL) ReleaseIconRef(gIconRef);
  431.     DeactivateControl(gPackageButton);
  432.     DeactivateControl(gFolderButton);
  433.     InvalWindowRect(GetDialogWindow(gPackageWindow), &gIconBox);
  434.     gFileInDisplay = false;
  435. }
  436.  
  437.  
  438.  
  439.  
  440. /* ActivatePackageWindow handles an activate event for the
  441.     package window. */
  442. void ActivatePackageWindow(WindowPtr target, Boolean activate) {
  443.     if (activate != gPWActive) {
  444.         gPWActive = activate;
  445.         if (gPWActive) {
  446.             if (gFileInDisplay) {
  447.                 ActivateControl(gPackageButton);
  448.                 ActivateControl(gFolderButton);
  449.             } else {
  450.                 DeactivateControl(gPackageButton);
  451.                 DeactivateControl(gFolderButton);
  452.             }
  453.             if (NavServicesAvailable()) {
  454.                 ActivateControl(gSelectButton);
  455.             } else DeactivateControl(gSelectButton);
  456.         } else {
  457.             DeactivateControl(gPackageButton);
  458.             DeactivateControl(gFolderButton);
  459.             DeactivateControl(gSelectButton);
  460.         }
  461.         DrawDialog(gPackageWindow);
  462.     }
  463. }
  464.  
  465.  
  466.  
  467.  
  468. /* PackageWindowUserItem draws the image in the drop box in the window.  If the window
  469.     is not the active window, then the image is drawn grayed out.  If appearance
  470.     is in use, then the drop box is drawn as a generic well. */
  471. static pascal void PackageWindowUserItem(DialogPtr theWindow, DialogItemIndex itemNo) {
  472.     RGBColor sForground, sBackground;
  473.     RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}, rgbBlack = {0, 0, 0}, rgbGray = {0x7FFF, 0x7FFF, 0x7FFF};
  474.     FSSpec target;
  475.     ThemeDrawState themeDrawState;
  476.     Boolean wasChanged;
  477.     WindowPtr wp;
  478.         /* set up */
  479.     wp = GetDialogWindow(theWindow);
  480.     SetPort(GetWindowPort(wp));
  481.         /* set the colors we're using for drawing */
  482.     GetForeColor(&sForground);
  483.     GetBackColor(&sBackground);
  484.     RGBForeColor(&rgbBlack);
  485.     RGBBackColor(&rgbWhite);
  486.         /* draw the frame */
  487.     themeDrawState = (gPWActive ? kThemeStateActive : kThemeStateInactive);
  488.     DrawThemeGenericWell(&gIconBox, themeDrawState,  true);
  489.         /* verify that we still have a file */
  490.     if (ResolveAlias(NULL, gFileAlias, &target, &wasChanged) != noErr) {
  491.         DisposeHandle((Handle) gFileAlias);
  492.         gFileAlias = NULL;
  493.         ReleaseIconRef(gIconRef);
  494.         gIconRef = NULL;
  495.         gFileInDisplay = false;
  496.     }
  497.         /* draw the file information */
  498.     if (gFileInDisplay) {
  499.         OSErr err;
  500.         short baseLine;
  501.         FontInfo fin;
  502.         Str255 name;
  503.             /* begin drawing */
  504.         TextFont(kFontIDGeneva); /* geneva */
  505.         TextSize(9);
  506.         GetFontInfo(&fin);
  507.             /* draw the icon image */
  508.         err = PlotIconRef(&gIconImage, kAlignNone, kTransformNone, kIconServicesNormalUsageFlag, gIconRef);
  509.             /* draw the file name */
  510.         baseLine = gIconImage.bottom + fin.ascent;
  511.         PLstrcpy(name, target.name);
  512.         TruncString(gIconBox.right - gIconBox.left - 4, name,  truncEnd);
  513.         MoveTo((gIconBox.left + gIconBox.right - StringWidth(name))/2, baseLine);
  514.         DrawString(name);
  515.             /* end drawing */
  516.         TextFont(systemFont); /* back to the system font */
  517.         TextSize(12);
  518.     } else {
  519.         Rect rPic;
  520.         rPic = (**gSplashPict).picFrame;
  521.         OffsetRect(&rPic, -rPic.left, -rPic.top);
  522.         OffsetRect(&rPic, (gIconBox.left + gIconBox.right - rPic.right)/2, (gIconBox.top + gIconBox.bottom - rPic.bottom)/2);
  523.         DrawPicture(gSplashPict, &rPic);
  524.     }
  525.         /* gray the image if we're in the background */
  526.     if ( !  gPWActive )
  527.         GrayOutBox(&gIconBox);
  528.         /* restore previous colors */
  529.     RGBForeColor(&sForground);
  530.     RGBBackColor(&sBackground);
  531. }
  532.  
  533.  
  534.  
  535.  
  536. /* VerifyPackage is called before we attempt to convert a folder into
  537.     a package.  This routine verifies that the contents of the folder are
  538.     formatted correctly and the environmental conditions are right
  539.     so that if the folder is turned into a package, the package will
  540.     be formatted correctly.  *packageFolder is a file specification record
  541.     pointing to the folder we would like to turn into a package.  *mustRebuildChain
  542.     will be set to true by this routine if (a) the package's main file is in
  543.     the package's root directory or (b) there is a chain of alias files
  544.     and one of those files along the way is outside of the package's folder
  545.     or is in the same directory as the previous one.  Normally, folder
  546.     packages aren't created, but or those intersted in experimenting
  547.     they can be created by holding down the option key.  if allowFolderPackages
  548.     is true, then will not abort if the main object in a package happens
  549.     to be a folder. */
  550. static Boolean VerifyPackage(FSSpec *packageFolder, Boolean *mustRebuildChain, Boolean allowFolderPackages) {
  551.     CInfoPBRec cat;
  552.     OSErr err;
  553.     long packageFolderDirID;
  554.     Str255 name, errstr;
  555.     FSSpec topAliasFile, nextAliasFile, prevAliasFile, mainTargetFile;
  556.     Boolean targetIsFolder, wasAliased;
  557.     long aliasCount;
  558.     
  559.         /* set up locals */
  560.     aliasCount = 0;
  561.     *mustRebuildChain = false;
  562.     
  563.         /* make sure file sharing is turned off */
  564.     if ( FileSharingAppIsRunning() ) {
  565.         GetIndString(errstr, kMainStringList, kFileSharingOn);
  566.         ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  567.         return false;
  568.     }
  569.         /* verify the package folder is a folder */
  570.     BlockZero(&cat, sizeof(cat));
  571.     cat.dirInfo.ioNamePtr = packageFolder->name;
  572.     cat.dirInfo.ioVRefNum = packageFolder->vRefNum;
  573.     cat.dirInfo.ioFDirIndex = 0;
  574.     cat.dirInfo.ioDrDirID = packageFolder->parID;
  575.     err = PBGetCatInfoSync(&cat);
  576.     if (err != noErr) return false;
  577.     if ((cat.dirInfo.ioFlAttrib & 16) == 0) {
  578.         GetIndString(errstr, kMainStringList, kNotAFolder);
  579.         ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  580.         return false;
  581.     }
  582.     packageFolderDirID = cat.dirInfo.ioDrDirID;
  583.     
  584.         /* if the bundle flag is set and we arrive here,
  585.         then we didn't recognize the package in the first
  586.         place.  That's an internal error. */
  587.     if ((cat.dirInfo.ioDrUsrWds.frFlags & kHasBundle) != 0) {
  588.         GetIndString(errstr, kMainStringList, kBundleAlreadySet);
  589.         ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  590.         return false;
  591.     }
  592.     
  593.         /* search for a top level alias file */
  594.     BlockZero(&cat, sizeof(cat));
  595.     cat.dirInfo.ioNamePtr = name;
  596.     cat.dirInfo.ioVRefNum = packageFolder->vRefNum;
  597.     cat.dirInfo.ioFDirIndex = 1;
  598.     cat.dirInfo.ioDrDirID = packageFolderDirID;
  599.     while (PBGetCatInfoSync(&cat) == noErr) {
  600.         if (((cat.dirInfo.ioFlAttrib & 16) == 0) && ((cat.dirInfo.ioDrUsrWds.frFlags & kIsAlias) != 0)) {
  601.                 /* increment the alias counter.  There can be only one. */
  602.             aliasCount += 1;
  603.             if (aliasCount > 1) {
  604.                 GetIndString(errstr, kMainStringList, kMoreThanOneAlias);
  605.                 ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  606.                 return false;
  607.             }
  608.                 /* get a reference to the alias file */
  609.             err = FSMakeFSSpec(packageFolder->vRefNum, packageFolderDirID, name, &topAliasFile);
  610.             if (err != noErr) return false;
  611.                 /* chain through all alias files one at a time making sure that each
  612.                 file is in the package heirarchy.  Set the *mustRebuildChain flag if
  613.                 any two files are in the same directory.  We must do this because
  614.                 relative alias files do not work when any two files in the chain
  615.                 are in the same directory.  */
  616.             nextAliasFile = topAliasFile;
  617.             while (true) {
  618.                     /* move on to the next file */
  619.                 prevAliasFile = nextAliasFile;
  620.                 err = ResolveAliasFile(&nextAliasFile, false, &targetIsFolder, &wasAliased);
  621.                 if (err != noErr) {
  622.                     GetIndString(errstr, kMainStringList, kBrokenAlias);
  623.                     ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  624.                     return false;
  625.                 }
  626.                     /* exit this loop once we've found the main target file */
  627.                 if ( ! wasAliased ) {
  628.                     mainTargetFile = nextAliasFile; /* we found the main target file */
  629.                     break;
  630.                 }
  631.                     /* if any one of the files is in the same directory as the previous one,
  632.                     then the alias chain needs to be rebuilt.... */
  633.                 if ( prevAliasFile.parID == nextAliasFile.parID ) {
  634.                     *mustRebuildChain |= true;
  635.                 }
  636.                     /* ......and, if it's an alias file somewhere between the first alias and
  637.                     the main target file AND it is outside of the package directory,
  638.                     then the alias chain needs to be rebuilt.  */
  639.                 if ( ! FSSpecIsInDirectory(&nextAliasFile, packageFolder->vRefNum, packageFolderDirID) ) {
  640.                     *mustRebuildChain |= true;
  641.                 }
  642.             }
  643.                 /* ....but we only need an alias chain if the package's main file
  644.                 is in the package's root directory.  i.e., it's in the same directory
  645.                 as the top level alias file. */
  646.             if ( mainTargetFile.parID != topAliasFile.parID ) {
  647.                 *mustRebuildChain = false;
  648.             }
  649.                 /* make sure the main target file is inside of the package directory */
  650.             if ( ! FSSpecIsInDirectory(&mainTargetFile, packageFolder->vRefNum, packageFolderDirID) ) {
  651.                 GetIndString(errstr, kMainStringList, kMainOutsideOfPackage);
  652.                 ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  653.                 return false;
  654.             }
  655.                 /* special permission (the option key) is required to make folder packages. */
  656.             if (targetIsFolder) {
  657.                     /* If we're allowing folder packages, make sure it's
  658.                     not a package folder. */
  659.                 if (allowFolderPackages) {
  660.                     CInfoPBRec targetCat;
  661.                     BlockZero(&targetCat, sizeof(targetCat));
  662.                     targetCat.dirInfo.ioNamePtr = mainTargetFile.name;
  663.                     targetCat.dirInfo.ioVRefNum = mainTargetFile.vRefNum;
  664.                     targetCat.dirInfo.ioFDirIndex = 0;
  665.                     targetCat.dirInfo.ioDrDirID = mainTargetFile.parID;
  666.                     err = PBGetCatInfoSync(&targetCat);
  667.                     if (err != noErr) return false;
  668.                     if ( ((targetCat.dirInfo.ioDrUsrWds.frFlags & kHasBundle) != 0) ) {
  669.                         GetIndString(errstr, kMainStringList, kAliasRefersToPackage);
  670.                         ParamAlert(kPackageDidNotVerify, mainTargetFile.name, errstr);
  671.                         return false;
  672.                     }
  673.                 } else {
  674.                     GetIndString(errstr, kMainStringList, kAliasRefersToFolder);
  675.                     ParamAlert(kPackageDidNotVerify, mainTargetFile.name, errstr);
  676.                     return false;
  677.                 }
  678.             }
  679.         } /* if alias */
  680.         cat.dirInfo.ioFDirIndex++;
  681.         cat.dirInfo.ioDrDirID = packageFolderDirID;
  682.     } /* of directory loop */
  683.     
  684.         /* make sure we found at least one alias */
  685.     if (aliasCount == 0) {
  686.         GetIndString(errstr, kMainStringList, kNoAliasPresent);
  687.         ParamAlert(kPackageDidNotVerify, packageFolder->name, errstr);
  688.         return false;
  689.     }
  690.         /* all tests successful */
  691.     return true;
  692. }
  693.  
  694.  
  695.  
  696.  
  697. /* FolderToPackage converts a folder into a Mac OS 9 package.  This routine
  698.     assumes the folder is set up correctly (i.e. VerifyPackage was called for
  699.     the folder and it returned true.  if constructChain is true, then a chain
  700.     of relative alias files will be created.  This should be the value returned
  701.     by VerifyPackage in the *mustRebuildChain parameter.  A chain of alias
  702.     files must be created when the package's main file is stored in the
  703.     package's root folder.  This is because there is a long standing Alias Manager
  704.     but that prevents relative alias files from being resolved when the
  705.     alias file and the target file are in the same folder. */
  706. static OSStatus FolderToPackage(FSSpec *packageFolder, Boolean constructChain) {
  707.     CInfoPBRec cat;
  708.     OSErr err;
  709.     FSSpec topAliasFile, mainTargetFile, chainAliasFile, chainAliasFolder;
  710.     long packageFolderDirID, chainAliasFolderDirID;
  711.     Boolean foundAlias, targetIsFolder, wasAliased;
  712.     Str255 name, folderName, aliasName, errstr;
  713.         /* set up locals */
  714.     foundAlias = false;
  715.         /* find out the folder's directory ID */
  716.     BlockZero(&cat, sizeof(cat));
  717.     cat.dirInfo.ioNamePtr = packageFolder->name;
  718.     cat.dirInfo.ioVRefNum = packageFolder->vRefNum;
  719.     cat.dirInfo.ioFDirIndex = 0;
  720.     cat.dirInfo.ioDrDirID = packageFolder->parID;
  721.     err = PBGetCatInfoSync(&cat);
  722.     if (err != noErr) return err;
  723.     packageFolderDirID = cat.dirInfo.ioDrDirID;
  724.         /* search for a top level alias file */
  725.     BlockZero(&cat, sizeof(cat));
  726.     cat.dirInfo.ioNamePtr = name;
  727.     cat.dirInfo.ioVRefNum = packageFolder->vRefNum;
  728.     cat.dirInfo.ioFDirIndex = 1;
  729.     cat.dirInfo.ioDrDirID = packageFolderDirID;
  730.     while (PBGetCatInfoSync(&cat) == noErr) {
  731.         if (((cat.dirInfo.ioFlAttrib & 16) == 0) && ((cat.dirInfo.ioDrUsrWds.frFlags & kIsAlias) != 0)) {
  732.             foundAlias = true;
  733.             break;
  734.         }
  735.         cat.dirInfo.ioFDirIndex++;
  736.         cat.dirInfo.ioDrDirID = packageFolderDirID;
  737.     }
  738.     if ( ! foundAlias) return paramErr;
  739.         /* resolve the alias file to the main target file */
  740.     err = FSMakeFSSpec(packageFolder->vRefNum, packageFolderDirID, name, &topAliasFile);
  741.     if (err != noErr) return err;
  742.     mainTargetFile = topAliasFile;
  743.     err = ResolveAliasFile(&mainTargetFile, true, &targetIsFolder, &wasAliased);
  744.     if (err != noErr) return err;
  745.         /* create a sub directory if needed */
  746.     if (constructChain) {
  747.             /* set up the folder FSSpec */
  748.         GetIndString(folderName, kMainStringList, kAliasRedirectFolderName);
  749.         err = FSMakeFSSpec(packageFolder->vRefNum, packageFolderDirID, folderName, &chainAliasFolder);
  750.         if (err == fnfErr) err = noErr;
  751.         if (err != noErr) return err;
  752.             /* find or create a sub directory for the indirect alias. */
  753.         err = FSpGetDirID(&chainAliasFolder, &chainAliasFolderDirID);
  754.         if (err != noErr)
  755.             err = FSpDirCreate(&chainAliasFolder, smSystemScript, &chainAliasFolderDirID);
  756.         if (err != noErr) {
  757.             GetIndString(errstr, kMainStringList, kErrorRedirectingAlias);
  758.             ParamAlert(kRerouteFailedAlert, errstr, NULL);
  759.             return err;
  760.         }
  761.             /* create a reference to the indirect alias file */
  762.         GetIndString(aliasName, kMainStringList, kRedirectAliasName);
  763.         err = FSMakeFSSpec(packageFolder->vRefNum, chainAliasFolderDirID, aliasName, &chainAliasFile);
  764.         if (err == fnfErr) err = noErr;
  765.         if (err != noErr) return err;
  766.             /* create an indirect alias file referencing the main target file */
  767.         err = UpdateRelativeAliasFile(&chainAliasFile, &mainTargetFile, true);
  768.         if (err != noErr) return err;
  769.             /* create an alias file referring to the indirect file. */
  770.         err = UpdateRelativeAliasFile(&topAliasFile, &chainAliasFile, false);
  771.         if (err != noErr) return err;
  772.     } else {
  773.             /* update the alias file to a relative path */
  774.         err = UpdateRelativeAliasFile(&topAliasFile, &mainTargetFile, false);
  775.         if (err != noErr) return err;
  776.     }
  777.         /* set the package flag */
  778.     cat.dirInfo.ioNamePtr = packageFolder->name;
  779.     cat.dirInfo.ioVRefNum = packageFolder->vRefNum;
  780.     cat.dirInfo.ioFDirIndex = 0;
  781.     cat.dirInfo.ioDrDirID = packageFolder->parID;
  782.     err = PBGetCatInfoSync(&cat);
  783.     if (err != noErr) return err;
  784.     cat.dirInfo.ioDrDirID = packageFolder->parID;
  785.     cat.dirInfo.ioDrUsrWds.frFlags |= kHasBundle;
  786.     err = PBSetCatInfoSync(&cat);
  787.     if (err != noErr) return err;
  788.     return noErr;
  789. }
  790.  
  791.  
  792.  
  793.  
  794. /* PackageToFolder converts a Mac OS 9 package into a regular
  795.     folder.  It does this by clearing the package bit in the folder's
  796.     Finder flags. */
  797. static OSStatus PackageToFolder(FSSpec *target) {
  798.     CInfoPBRec cat;
  799.     OSErr err;
  800.     cat.dirInfo.ioNamePtr = target->name;
  801.     cat.dirInfo.ioVRefNum = target->vRefNum;
  802.     cat.dirInfo.ioFDirIndex = 0;
  803.     cat.dirInfo.ioDrDirID = target->parID;
  804.     err = PBGetCatInfoSync(&cat);
  805.     if (err != noErr) return err;
  806.     cat.dirInfo.ioDrDirID = target->parID;
  807.     cat.dirInfo.ioDrUsrWds.frFlags &= ~ kHasBundle;
  808.     err = PBSetCatInfoSync(&cat);
  809.     return err;
  810. }
  811.  
  812.  
  813.  
  814.  
  815.  
  816. /* HitPackageWindow is called when the dialog manager's DialogSelect indicates
  817.     that an item in the main finder drag pro window has been hit.  Here,
  818.     either we begin a drag, or we adjust the promise/regular hfs controls */
  819. void HitPackageWindow(DialogPtr theDialog, EventRecord *event, short itemNo) {
  820.     switch (itemNo) {
  821.         case kPackageUserItem:
  822.             break;
  823.             
  824.         case kFolderItem: /* turn it into a folder */
  825.             if (gFolderTypeSelection != kFolderIsFolder) {
  826.                 FSSpec target;
  827.                 Boolean wasChanged;
  828.                 if (ResolveAlias(NULL, gFileAlias, &target, &wasChanged) != noErr) {
  829.                     SetNewDisplay(NULL);
  830.                 } else {
  831.                     if (PackageToFolder(&target) == noErr) {
  832.                         SetNewDisplay(&target); /* update the view */
  833.                         ShowChangesInFinderWindow(target.vRefNum, target.parID);
  834.                     }
  835.                 }
  836.             }
  837.             break;
  838.             
  839.         case kPackageItem: /* turn it into a package */
  840.             if (gFolderTypeSelection != kFolderIsPackage) {
  841.                 FSSpec target;
  842.                 Boolean wasChanged;
  843.                 if (ResolveAlias(NULL, gFileAlias, &target, &wasChanged) != noErr) {
  844.                     SetNewDisplay(NULL);
  845.                 } else {
  846.                     Boolean mustRebuildChain;
  847.                     if ( VerifyPackage(&target, &mustRebuildChain, ((event->modifiers & optionKey) != 0)) ) {
  848.                         OSStatus err;
  849.                         err = FolderToPackage(&target, mustRebuildChain);
  850.                         if (err != noErr) {
  851.                             Str255 errStr;
  852.                             NumToString(err, errStr);
  853.                             ParamAlert(kFailedToCreatePackage, errStr, target.name);
  854.                         } else {
  855.                             SetNewDisplay(&target); /* update the view */
  856.                             ShowChangesInFinderWindow(target.vRefNum, target.parID);
  857.                         }
  858.                     }
  859.                 }
  860.             }
  861.             break;
  862.             
  863.         case kSelectButtonItem:    /* call the select routine defined in PackageTool.c */
  864.             SelectFolderOrPackage();
  865.             break;
  866.     }
  867. }
  868.  
  869.  
  870.  
  871.  
  872. /* CreatePackageWindow creates the package window.  If it cannot be
  873.     created, then an error is returned. */
  874. OSStatus CreatePackageWindow(void) {
  875.     OSErr err;
  876.     Boolean installedTracker, installedReceiver;
  877.     short itemt;
  878.     Rect itemb;
  879.     Handle itemh;
  880.     DialogPtr dialog;
  881.     long itemSize;
  882.     Point where;
  883.     
  884.         /* set up locals for recovery */
  885.     dialog = NULL;
  886.     installedTracker = installedReceiver = false;
  887.     
  888.         /* get other resources */
  889.     gSplashPict = GetPicture(kSpashPictResource);
  890.     if (gSplashPict == NULL) { err = resNotFound; goto bail; }
  891.         /* create the dialog */
  892.     dialog = GetNewDialog(kPackageDialogResource, NULL, (WindowPtr) (-1));    
  893.     if (dialog == NULL) { err = memFullErr; goto bail; }
  894.     
  895.         /* grab and set up our dialog items */
  896.     GetDialogItem(dialog, kPackageUserItem, &itemt, (Handle*) &itemh, &gIconBox);
  897.     myUserItem = NewUserItemUPP(PackageWindowUserItem);
  898.     if (myUserItem == NULL) { err = memFullErr; goto bail; }
  899.     SetDialogItem(dialog, kPackageUserItem, userItem, (Handle) myUserItem, &gIconBox);
  900.     GetDialogItem(dialog, kFolderItem, &itemt, (Handle*) &gFolderButton, &itemb);
  901.     GetDialogItem(dialog, kPackageItem, &itemt, (Handle*) &gPackageButton, &itemb);
  902.     GetDialogItem(dialog, kSelectButtonItem, &itemt, (Handle*) &gSelectButton, &itemb);
  903.         
  904.         /* set initial control values */
  905.     SetControlValue(gPackageButton, (gFolderTypeSelection == kFolderIsPackage ? 1 : 0));
  906.     SetControlValue(gFolderButton, (gFolderTypeSelection == kFolderIsFolder ? 1 : 0));
  907.     DeactivateControl(gPackageButton);
  908.     DeactivateControl(gFolderButton);
  909.  
  910.         /* calculate the drawn icon's boundary */
  911.     SetRect(&gIconImage, 0, 0, 32, 32);
  912.     OffsetRect(&gIconImage, (gIconBox.right + gIconBox.left - 32) / 2, gIconBox.top + 32);
  913.  
  914.         /* install the drag handlers */
  915.     gMainTrackingHandler = NewDragTrackingHandlerProc(MyDragTrackingHandler);
  916.     if (gMainTrackingHandler == NULL) { err = memFullErr; goto bail; }
  917.     err = InstallTrackingHandler(gMainTrackingHandler, GetDialogWindow(dialog), NULL);
  918.     if (err != noErr) { err = memFullErr; goto bail; }
  919.     installedTracker = true;
  920.  
  921.     gMainReceiveHandler = NewDragReceiveHandlerProc(MyDragReceiveHandler);
  922.     if (gMainReceiveHandler == NULL) { err = memFullErr; goto bail; }
  923.     err = InstallReceiveHandler(gMainReceiveHandler, GetDialogWindow(dialog), NULL);
  924.     if (err != noErr) { err = memFullErr; goto bail; }
  925.     installedReceiver = true;
  926.  
  927.         /* position the window, if necessary */
  928.     if (GetCollectionItem(GetCollectedPreferences(),  'QDPt', 1, (itemSize = sizeof(where), &itemSize),  &where) == noErr) {
  929.         MoveWindow(GetDialogWindow(dialog), where.h, where.v, true);
  930.     }
  931.         /* done, window complete */
  932.     ShowWindow(GetDialogWindow(dialog));
  933.     gPackageWindow = dialog;
  934.     return noErr;
  935.  
  936. bail:
  937.     if (installedReceiver)
  938.         RemoveReceiveHandler(gMainReceiveHandler, GetDialogWindow(dialog));
  939.     if (installedTracker)
  940.         RemoveTrackingHandler(gMainTrackingHandler, GetDialogWindow(dialog));
  941.     if (dialog != NULL) DisposeDialog(dialog);
  942.     return err;
  943. }
  944.  
  945.  
  946.  
  947.  
  948.  
  949. /* ClosePackageWindow closes the package window and disposes of
  950.     any structures allocated when it was opened. */
  951. void ClosePackageWindow(void) {
  952.     Point where;
  953.     WindowPtr wp;
  954.     CGrafPtr gp;
  955.     Rect r;
  956.     wp = GetDialogWindow(gPackageWindow);
  957.     gp = GetWindowPort(wp);
  958.     SetPort(gp);
  959.     GetPortBounds(gp, &r);
  960.     where = * (Point*) &r;
  961.     LocalToGlobal(&where);
  962.     AddCollectionItem(GetCollectedPreferences(), 'QDPt', 1, sizeof(where), &where);
  963.     SetNewDisplay(NULL);
  964.     DisposeDialog(gPackageWindow);
  965. }
  966.  
  967.